#include "gamedata.h"

QHash<QString, QPixmap*> GameData::interfaceTextures;
QHash<QString, QPixmap*> GameData::floorTextures;
QHash<QString, QPixmap*> GameData::wallTextures;
QHash<QString, QPixmap*> GameData::doorTextures;
QHash<QString, QPixmap*> GameData::objectTextures;
QHash<QString, QPixmap*> GameData::itemTextures;

QList<QPixmap*> GameData::avatarN;
QList<QPixmap*> GameData::avatarE;
QList<QPixmap*> GameData::avatarS;
QList<QPixmap*> GameData::avatarW;

GameData::GameData() {}

void GameData::importTextures() {    
    // lets get those interface textures
    QDir interfaceDir("textures/interface");
    QFileInfoList interfaceFiles = interfaceDir.entryInfoList();

    for(int i = 0; i < interfaceFiles.size(); ++i) {
        QFileInfo file = interfaceFiles[i];
        if(file.isFile() && file.fileName().contains(".png")) {
            interfaceTextures[file.baseName()] = new QPixmap("textures/interface/" + file.fileName());
        }
    }

    // lets grab the floors now
    QDir floorDir("textures/floors");
    QFileInfoList floorFiles = floorDir.entryInfoList();

    for(int i = 0; i < floorFiles.size(); ++i) {
        QFileInfo file = floorFiles[i];
        if(file.isFile() && file.fileName().contains(".png")) {
            floorTextures[file.baseName()] = new QPixmap("textures/floors/" + file.fileName());
        }
    }

    // grab the walls next (**make sure each wall type has a .wall file)
    QDir wallDir("textures/walls");
    QFileInfoList wallFiles = wallDir.entryInfoList();

    for(int i = 0; i < wallFiles.size(); ++i) {
        QFileInfo file = wallFiles[i];
        if(file.isFile()) {
            if(file.fileName().contains(".png")) {
                wallTextures[file.baseName()] = new QPixmap("textures/walls/" + file.fileName());
            }
        }
    }

    // grab the doors
    QDir doorDir("textures/doors");
    QFileInfoList doorFiles = doorDir.entryInfoList();

    for(int i = 0; i < doorFiles.size(); ++i) {
        QFileInfo file = doorFiles[i];
        if(file.isFile()) {
            if(file.fileName().contains(".png")) {
                doorTextures[file.baseName()] = new QPixmap("textures/doors/" + file.fileName());
            }
        }
    }

    // grab the objects
    QDir objectDir("textures/objects");
    QFileInfoList objectFiles = objectDir.entryInfoList();

    for(int i = 0; i < objectFiles.size(); ++i) {
        QFileInfo file = objectFiles[i];
        if(file.isFile() && file.fileName().contains(".png")) {
            objectTextures[file.baseName()] = new QPixmap("textures/objects/" + file.fileName());
        }
    }

    // now the items!
    QDir itemDir("textures/items");
    QFileInfoList itemFiles = itemDir.entryInfoList();

    for(int i = 0; i < itemFiles.size(); ++i) {
        QFileInfo file = itemFiles[i];
        if(file.isFile() && file.fileName().contains(".png")) {
            itemTextures[file.baseName()] = new QPixmap("textures/items/" + file.fileName());
        }
    }

    // and last but not least, the most complicated: the avatar
    QPixmap fullAvatar("textures/avatar/avatar.png");

    for(int y = 0; y < 4; ++y) {
        for(int x = 0; x < 4; ++x) {
            avatarN << new QPixmap(fullAvatar.copy((x * 64), (y * 64), 64, 64));
        }
    }
    for(int x = 7; x > 3; --x) {
        for(int y = 0; y < 4; ++y) {
            avatarE << new QPixmap(fullAvatar.copy((x * 64), (y * 64), 64, 64));
        }
    }
    for(int y = 7; y > 3; --y) {
        for(int x = 7; x > 3; --x) {
            avatarS << new QPixmap(fullAvatar.copy((x * 64), (y * 64), 64, 64));
        }
    }
    for(int x = 0; x < 4; ++x) {
        for(int y = 7; y > 3; --y) {
            avatarW << new QPixmap(fullAvatar.copy((x * 64), (y * 64), 64, 64));
        }
    }
}

Map* GameData::importMap(QString mapName) {
    QFile file(mapName + ".bin");
    file.open(QIODevice::ReadOnly);
    QDataStream in(&file);
    in.setVersion(QDataStream::Qt_4_6);

    // general properties
    int mapWidth, mapHeight;
    QString wallType;

    // tile properties
    QString floorType;
    QString objTexture;
    EnvObject *object;
    QString itemTexture;
    Item *item;
    int pivotDoorCount;
    QString pivotDoorTexture;
    int pivotDoorLoc;
    PivotDoor *pivotDoor;
    bool wallB, wallR;

    in >> mapWidth >> mapHeight >> wallType;
    Tile ***tileArray = new Tile**[mapWidth]; // allocate memory for entire map

    for(int x = 0; x < mapWidth; ++x) {
        tileArray[x] = new Tile*[mapHeight];
        for(int y = 0; y < mapHeight; ++y) {
            // read the floor texture
            in >> floorType;

            // read the object texture and properties
            in >> objTexture;
            if(objTexture == "none") {
                object = NULL;
            }
            else if(objTexture.contains("boulder")) {
                object = new BoulderObject(objTexture);
            }
            else if(objTexture.contains("gate")) {
                object = new GateObject(objTexture);
            }
            else if(objTexture.contains("mirror")) {
                object = new MirrorObject(objTexture);
            }
            else if(objTexture.contains("lasergun")) {
                object = new LaserGunObject(objTexture);
            }
            else if(objTexture.contains("laserreceiver")) {
                QPoint activationPoint;
                in >> activationPoint;

                object = new LaserReceiverObject(objTexture, activationPoint);
            }
            else if(objTexture.contains("portal")) {
                QString destinationMap;
                int linkCode;

                in >> destinationMap;
                in >> linkCode;

                object = new PortalObject(objTexture, destinationMap, linkCode);
            }

            // read item texture and properties
            in >> itemTexture;
            if(itemTexture == "none") item = NULL;
            else item = new Item(itemTexture);

            // read pivot doors
            QList<PivotDoor*> *pivotDoors = new QList<PivotDoor*>();

            in >> pivotDoorCount;
            for(int i = 0; i < pivotDoorCount; ++i) {
                in >> pivotDoorTexture;
                in >> pivotDoorLoc;

                pivotDoor = new PivotDoor(pivotDoorTexture, pivotDoorLoc);
                *pivotDoors << pivotDoor;
            }

            // read wall bools
            in >> wallB >> wallR;

            // instantiate tile object and put in array
            tileArray[x][y] = new Tile(floorType, object, item, pivotDoors, wallB, wallR);
        }
    }

    file.close();
    Map *map = new Map(QFileInfo(file).baseName(), tileArray, mapWidth, mapHeight, wallType);
    return map;
}

void GameData::exportMap(Map *map, QString destination) {
    if(map != NULL) {
        QFile file(destination + map->getName() + ".bin");
        file.resize(0);
        file.open(QIODevice::WriteOnly);
        QDataStream out(&file);
        out.setVersion(QDataStream::Qt_4_6);

        out << map->getWidth() << map->getHeight() << map->getWallType();

        Tile *tile;
        for(int x = 0; x < map->getWidth(); ++x) {
            for(int y = 0; y < map->getHeight(); ++y) {
                tile = map->getTileAt(x,y);

                // write the floor texture
                out << tile->getFloorType();

                // write the object
                EnvObject *object = tile->getObject();
                if(object) {
                    QString objTexture = object->getTexture();
                    if(objTexture.contains("boulder") || objTexture.contains("gate") || objTexture.contains("mirror") || objTexture.contains("lasergun")) {
                        out << objTexture;
                    }
                    else if(objTexture.contains("laserreceiver")) {
                        LaserReceiverObject *laserReceiver = dynamic_cast<LaserReceiverObject *>(object);

                        out << laserReceiver->getTexture();
                        out << laserReceiver->getActivationPoint();
                    }
                    else if(objTexture.contains("portal")) {
                        PortalObject *portal = dynamic_cast<PortalObject *>(object);
                        out << portal->getTexture();
                        out << portal->getDestinationMap();
                        out << portal->getLinkCode();
                    }
                }
                else out << QString("none");

                // write the item
                Item *item = tile->getItem();
                if(item) out << item->getTexture();
                else out << QString("none");

                //write the pivot doors
                QList<PivotDoor*> *pivotDoors = tile->getPivotDoors();
                out << pivotDoors->size();

                for(int i = 0; i < pivotDoors->size(); ++i) {
                    out << pivotDoors->at(i)->getTexture();
                    out << pivotDoors->at(i)->getLocation();
                }

                // write the wall bools
                out << tile->getWallB() << tile->getWallR();
            }
        }
        file.close();
    }
}

Player* GameData::importPlayerData(QString username) {
    QFile file("saves/" + username + "/data.dat");

    file.open(QIODevice::ReadOnly);
    QDataStream in(&file);
    in.setVersion(QDataStream::Qt_4_6);

    int difficulty;
    QString curMap;
    int posX, posY;
    int direction;
    int inventorySize;
    QString itemTexture;
    QList<Item*> *inventory = new QList<Item*>;

    in >> difficulty;
    in >> curMap;
    in >> posX >> posY;
    in >> direction;

    in >> inventorySize;
    for(int i = 0; i < inventorySize; ++i) {
        in >> itemTexture;
        Item *item = new Item(itemTexture);
        inventory->push_back(item);
    }

    file.close();

    Player *player = new Player(username, difficulty, curMap, posX, posY, direction, inventory);
    return player;
}

void GameData::exportSaveFile(Player *player) {
    QFile file("saves/" + player->getUsername() + "/data.dat");
    file.resize(0);
    file.open(QIODevice::WriteOnly);
    QDataStream out(&file);
    out.setVersion(QDataStream::Qt_4_6);

    out << player->getDifficulty();
    out << player->getCurMap();
    out << player->getPosX();
    out << player->getPosY();
    out << player->getDirection();

    QList<Item*> *inventory = player->getInventory();
    out << inventory->size();
    for(int i = 0; i < inventory->size(); ++i) {
        out << inventory->at(i)->getTexture();
    }

    file.close();
}

/*!
   Delete a directory along with all of its contents.

   \param dirPath Path of directory to remove.
   \return true on success; false on error.
*/
bool GameData::removeDir(const QString dirPath)
{
    QDir dir(dirPath);
    if (!dir.exists()) return true;

    foreach(const QFileInfo &info, dir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot)) {
        if (info.isDir()) {
            if (!removeDir(info.filePath())) return false;
        } else {
            if (!dir.remove(info.fileName())) return false;
        }
    }

    QDir parentDir(QFileInfo(dirPath).path());
    return parentDir.rmdir(QFileInfo(dirPath).fileName());
}

/*!
   Copies the contents from one directory to another.

   \param srcPath Path of source directory
   \param dstPath Path of destination directory
   \return true on success; false on error.
*/

bool GameData::copyDir(QString srcPath, QString dstPath) {
    removeDir(dstPath);

    QDir parentDstDir(QFileInfo(dstPath).path());
    if (!parentDstDir.mkdir(QFileInfo(dstPath).fileName())) return false;

    QDir srcDir(srcPath);
    foreach(const QFileInfo &info, srcDir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot)) {
        QString srcItemPath = srcPath + "/" + info.fileName();
        QString dstItemPath = dstPath + "/" + info.fileName();
        if (info.isDir()) {
            if (!copyDir(srcItemPath, dstItemPath)) return false;

        } else if (info.isFile()) {
            if (!QFile::copy(srcItemPath, dstItemPath)) return false;

        } else qDebug() << "Unhandled item" << info.filePath() << "in copyDir";
    }
    return true;
}
